package com.example.mydemoproject.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.example.mydemoproject.config.exception.BusinessException;
import com.example.mydemoproject.config.exception.ServiceException;
import com.example.mydemoproject.dao.QuartzJobMapper;
import com.example.mydemoproject.model.constant.CommonConstant;
import com.example.mydemoproject.model.entity.QuartzJob;
import com.example.mydemoproject.model.request.QuartzJobReqVo;
import lombok.RequiredArgsConstructor;
import lombok.SneakyThrows;
import org.quartz.*;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.util.Date;

/**
 * @author LiuZhiYong
 * @description
 * @date 2022-11-25
 **/
@Service
@RequiredArgsConstructor
public class QuartzJobService extends ServiceImpl<QuartzJobMapper, QuartzJob> {

    private final Scheduler scheduler;

    /**
     * 立即执行的任务分组
     */
    private static final String JOB_TEST_GROUP = "test_group";

    /**
     * 添加定时任务
     *
     * @param quartzJobReqVo 定时任务信息
     *
     * @author liuzhiyong
     * @date 2022/11/25
     */
    @Transactional(rollbackFor = Exception.class)
    public void addQuartzJob(QuartzJobReqVo quartzJobReqVo) {
        // 校验是否有重复的任务类名
        LambdaQueryWrapper<QuartzJob> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(QuartzJob::getJobClassName, quartzJobReqVo.getJobClassName())
                .eq(QuartzJob::getDelFlag, CommonConstant.DEL_FLAG_0);
        QuartzJob queryOne = this.baseMapper.selectOne(wrapper);
        if (queryOne != null) {
            throw new BusinessException("该定时任务的类名已存在!");
        }
        // 任务数据入库
        QuartzJob quartzJob = new QuartzJob();
        BeanUtils.copyProperties(quartzJobReqVo, quartzJob);
        this.baseMapper.insert(quartzJob);
        // 如果任务状态正常, 任务添加到调度器
        if (CommonConstant.STATUS_NORMAL.equals(quartzJobReqVo.getStatus())) {
            this.schedulerAdd(quartzJobReqVo.getJobClassName(), quartzJobReqVo.getCronExpression(), quartzJobReqVo.getParameter());
        }
    }

    /**
     * 编辑任务
     *
     * @param quartzJobReqVo    任务信息
     *
     * @author liuzhiyong
     * @date 2022/11/25
     */
    @Transactional(rollbackFor = Exception.class)
    @SneakyThrows
    public void editQuartzJob(QuartzJobReqVo quartzJobReqVo) {
        String jobClassName = quartzJobReqVo.getJobClassName();
        // 更新DB中的任务信息
        QuartzJob quartzJob = new QuartzJob();
        BeanUtils.copyProperties(quartzJobReqVo, quartzJob);
        int i = this.baseMapper.updateById(quartzJob);
        if (i <= 0) {
            throw new BusinessException("更新失败,未匹配到定时任务!");
        }
        if (CommonConstant.STATUS_NORMAL.equals(quartzJob.getStatus())) {
            // 如果任务状态为正常, 删除调度器中旧的任务, 添加新的任务
            schedulerDelete(jobClassName);
            this.schedulerAdd(jobClassName, quartzJobReqVo.getCronExpression(), quartzJobReqVo.getParameter());
        } else {
            // 任务状态为停止, 暂停任务
            schedulerDelete(jobClassName);
        }
    }

    /**
     * 根据id删除并停止任务
     *
     * @param id    主键id
     *
     * @author liuzhiyong
     * @date 2022/11/25
     */
    @Transactional(rollbackFor = Exception.class)
    public void removeQuartzJob(String id) {
        // 查询任务
        QuartzJob quartzJob = this.baseMapper.selectById(id);
        if (quartzJob == null) {
            throw new BusinessException("该任务不存在");
        }
        // 删除DB任务数据
        this.baseMapper.deleteById(id);
        // 删除调度器中的任务
        this.schedulerDelete(quartzJob.getJobClassName());
    }

    /**
     * 暂停定时任务
     *
     * @param id    任务id
     *
     * @author liuzhiyong
     * @date 2022/11/25
     */
    @Transactional(rollbackFor = Exception.class)
    public void pauseJob(String id) {
        // 查询DB信息
        LambdaQueryWrapper<QuartzJob> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(QuartzJob::getId, id)
                .eq(QuartzJob::getStatus, CommonConstant.STATUS_NORMAL);
        QuartzJob quartzJob = this.baseMapper.selectOne(wrapper);
        if (quartzJob == null) {
            throw new BusinessException("当前任务已暂停");
        }
        // 更新DB状态
        QuartzJob updateQuartzJob = new QuartzJob();
        updateQuartzJob.setId(id);
        updateQuartzJob.setStatus(CommonConstant.STATUS_DISABLE);
        this.baseMapper.updateById(updateQuartzJob);
        // 暂停任务
        this.schedulerDelete(quartzJob.getJobClassName());
    }

    /**
     * 恢复定时任务
     *
     * @param id    任务id
     *
     * @author liuzhiyong
     * @date 2022/11/25
     */
    public void resumeJob(String id) {
        //查询DB信息
        LambdaQueryWrapper<QuartzJob> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(QuartzJob::getId, id)
                .eq(QuartzJob::getStatus, CommonConstant.STATUS_DISABLE);
        QuartzJob quartzJob = this.baseMapper.selectOne(wrapper);
        if (quartzJob == null) {
            throw new BusinessException("当前任务已运行");
        }
        // 更新DB状态
        QuartzJob updateQuartzJob = new QuartzJob();
        updateQuartzJob.setId(id);
        updateQuartzJob.setStatus(CommonConstant.STATUS_NORMAL);
        this.baseMapper.updateById(updateQuartzJob);
        // 删除调度器任务
        schedulerDelete(quartzJob.getJobClassName());
        // 添加调度器任务
        this.schedulerAdd(quartzJob.getJobClassName(), quartzJob.getCronExpression(), quartzJob.getParameter());
    }

    /**
     * 执行一次任务
     *
     * @param id    任务id
     *
     * @author liuzhiyong
     * @date 2022/11/26
     */
    @SneakyThrows
    public void excute(String id) {
        QuartzJob quartzJob = this.baseMapper.selectById(id);
        if (quartzJob == null) {
            throw new BusinessException("任务不存在,或是已被删除!");
        }
        String jobClassName = quartzJob.getJobClassName();
        LocalDateTime now = LocalDateTime.now();
        String ymd = now.format(DateTimeFormatter.ofPattern("yyyyMMddHHmmss"));
        String identity = jobClassName + ymd;
        //3秒后执行一次
        LocalDateTime threeSecondAfter = now.plusSeconds(3L);
        // 定义一个Trigger 指定时间触发,不重复
        SimpleTrigger trigger = (SimpleTrigger)TriggerBuilder.newTrigger()
                .withIdentity(identity, JOB_TEST_GROUP)
                .startAt(Date.from(threeSecondAfter.atZone(ZoneId.systemDefault()).toInstant()))
                .build();
        // 构建job信息
        JobDetail jobDetail = JobBuilder
                .newJob(this.getJobInstance(jobClassName).getClass())
                .withIdentity(identity)
                .usingJobData("parameter", quartzJob.getParameter())
                .build();
        // 将trigger和 jobDetail 加入这个调度
        scheduler.scheduleJob(jobDetail, trigger);
        // 启动scheduler
        scheduler.start();
    }


    /**
     * 添加定时任务
     *
     * @param jobClassName      任务类名
     * @param cronExpression    corn表达式
     * @param parameter         参数
     *
     * @author liuzhiyong
     * @date 2022/11/25
     */
    private void schedulerAdd(String jobClassName, String cronExpression, String parameter) {
        try {
            // 启动调度器
            scheduler.start();
            // 构建job信息
            JobDetail jobDetail = JobBuilder
                    .newJob(this.getJobInstance(jobClassName).getClass())
                    .withIdentity(jobClassName)
                    .usingJobData("parameter", parameter)
                    .build();
            // 表达式调度构建器(即任务的执行时间)
            CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(cronExpression);
            // 按新的cron表达式构建一个新的cron触发器 触发器有两种CronTrigger 和 SimpleTrigger
            CronTrigger cronTrigger = TriggerBuilder
                    .newTrigger()
                    .withIdentity(jobClassName)
                    .withSchedule(scheduleBuilder)
                    .build();
            // 添加到调度器
            scheduler.scheduleJob(jobDetail, cronTrigger);
        } catch (SchedulerException e) {
            throw new ServiceException("创建定时任务失败", e);
        } catch (ClassNotFoundException | InstantiationException | IllegalAccessException e) {
            throw new BusinessException("任务类名错误, 后台未找到类名", e);
        }
    }

    /**
     * 删除任务
     *
     * @param jobClassName  任务类名
     *
     * @author liuzhiyong
     * @date 2022/11/25
     */
    @SneakyThrows
    private void schedulerDelete(String jobClassName) {
        // 暂停触发器
        scheduler.pauseTrigger(TriggerKey.triggerKey(jobClassName));
        // 从调度器中删除指定的触发器
        scheduler.unscheduleJob(TriggerKey.triggerKey(jobClassName));
        // 删除job
        scheduler.deleteJob(JobKey.jobKey(jobClassName));
    }

    /**
     * 暂停任务
     *
     * @param jobClassName  任务类名
     *
     * @author liuzhiyong
     * @date 2022/11/25
     */
    @SneakyThrows
    private void schedulerPause(String jobClassName) {
        scheduler.pauseJob(JobKey.jobKey(jobClassName.trim()));
    }

    /**
     * 根据全限定类名获取Job实例
     *
     * @param className 任务类名
     * @return org.quartz.Job
     * @author liuzhiyong
     * @date 2022/11/25
     */
    private Job getJobInstance(String className) throws ClassNotFoundException,
            InstantiationException,
            IllegalAccessException {
        Class<?> aClass = Class.forName(className);
        return (Job)aClass.newInstance();
    }

}
